/*!
 * 文件名称： main.c
 * 描    述： RCU-时钟配置及输出实验主函数文件
 * 版本：     2023-12-03, V1.0
*/

/*
* GD32F303东方红开发板V1.0
* 淘宝商城：   https://juwo.taobao.com
* 技术交流：   https://www.gd32bbs.com/ask/
* 视频学习：   https://space.bilibili.com/475462605
* 微信公众号： gd32bbs
* Copyright    苏州聚沃电子科技有限公司
* 版权所有，盗版必究。
*/

#include "gd32f30x.h"
#include <stdio.h>
#include "bsp_led.h"
#include "driver_public.h"
#include "bsp_uart.h"

void rcu_system_clk_config_120M(void);
void ckout_config(void);

/*!
* 说明     main函数
* 输入     无
* 返回值   无
*/

int main(void)
{
    driver_init();
    bsp_uart_init(&BOARD_UART);	   															/* 板载UART初始化 */
    printf_log("Example of Clock configuration and output.\r\n");
    rcu_system_clk_config_120M();    														/*使用外部8M晶振进行倍频，将系统时钟配置为120M*/
	
	ckout_config();  																					  /*通过PA8（CKOUT）将系统时钟输出*/
	while (1)
	{}
}


/*!
* 说明     使用外部8M晶振进行倍频，将系统时钟配置为120M函数
* 输入     无
* 返回值   无
*/
void rcu_system_clk_config_120M(void)
{
	  uint32_t timeout = 0U;
    uint32_t stab_flag = 0U;

    RCU_CTL |= RCU_CTL_HXTALEN;    /* 使能外部8M晶振 */

    /* 等待外部晶振ready，或者外部晶振未ready情况下超时退出 */
    do{
        timeout++;
        stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
    }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));

    /* 如果外部晶振未ready，程序卡死进入while(1)，读者若避免卡死可在此处切换到内部振荡器使用 */
    if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
        while(1){
        }
    }

    RCU_APB1EN |= RCU_APB1EN_PMUEN;
    PMU_CTL |= PMU_CTL_LDOVS;

    /* AHB一分频，系统时钟等于AHB时钟 */
    RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
    /* APB2一分频，APB2高速外设时钟等于AHB时钟 */
    RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
    /* APB1 二分频，APB1低速外设时钟等于AHB时钟/2 */
    RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;

    /* 选择HXTAL/2作为PLL时钟源，开发板外部晶振为8M，即8/2=4M作为PLL时钟源*/
    RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0);
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0);

    /* CK_PLL = (CK_HXTAL/2) * 30 = 120 MHz */
		/* 若希望调整系统时钟，可以调整RCU_PLL_MUL30倍频因子即可 */
    RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5);
    RCU_CFG0 |= RCU_PLL_MUL30;

    /* 使能 PLL */
    RCU_CTL |= RCU_CTL_PLLEN;

    /* 等待PLL Ready */
    while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
    }

    /* 使能PLL高驱模式 */
    PMU_CTL |= PMU_CTL_HDEN;
    while(0U == (PMU_CS & PMU_CS_HDRF)){
    }

    /* 选择PLL高驱模式 */
    PMU_CTL |= PMU_CTL_HDS;
    while(0U == (PMU_CS & PMU_CS_HDSRF)){
    }

    /* 选择PLL作为系统时钟 */
    RCU_CFG0 &= ~RCU_CFG0_SCS;
    RCU_CFG0 |= RCU_CKSYSSRC_PLL;

    /* 等待PLL被选择作为系统时钟 */
    while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
    }
}

/*!
* 说明     ckout输出配置函数
* 输入     无
* 返回值   无
*/
void ckout_config(void)
{
	/* 以下两句配置PA8作为推挽输出，即作为CKOUT功能输出 */
	rcu_periph_clock_enable(RCU_GPIOA);
	gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_MAX,GPIO_PIN_8);
	
	/* 以下语句为配置系统时钟输出到CKOUT引脚，若希望输出其他时钟可以修改该函数形参，
	在GD32F303上仅可以输出以下时钟：RCU_CKOUT0SRC_CKSYS，RCU_CKOUT0SRC_IRC8M，RCU_CKOUT0SRC_HXTAL，RCU_CKOUT0SRC_CKPLL_DIV2 */
	rcu_ckout0_config(RCU_CKOUT0SRC_CKSYS);
}

